package ddz.logic.game;

import ch.qos.logback.classic.Logger;
import com.kaka.notice.Command;
import com.kaka.notice.FacadeFactory;
import com.kaka.notice.Message;
import com.kaka.notice.annotation.Handler;
import com.kaka.util.MathUtils;
import ddz.constants.Crypto;
import ddz.constants.OpCode;
import ddz.core.*;
import ddz.core.ai.AI;
import ddz.core.ai.AIBot;
import ddz.core.ai.AILaizi;
import ddz.db.dao.TableDao;
import ddz.db.dao.UserDao;
import ddz.db.entity.ItemInfo;
import ddz.db.entity.UserInfo;
import ddz.db.service.TableTokenService;
import ddz.logic.ItemUtils;
import ddz.net.WsSender;
import ddz.numerical.manager.ConfTimeoutInfoManager;
import ddz.numerical.pojo.ConfTimeoutInfo;
import ddz.protos.Game;
import ddz.protos.GameOpts;
import ddz.scheduler.QuartzFacade;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 显示玩家操作按钮
 *
 * @author zkpursuit
 */
@Handler(cmd = OpCode.cmd_show_opts, type = String.class)
public class ShowOptsHandler extends Command implements WsSender {

    private final static Logger logger = (Logger) LoggerFactory.getLogger(ShowOptsHandler.class);

    @Override
    public void execute(Message msg) {
        Table table = (Table) msg.getBody();
        if (table == null) {
            return;
        }
        if (table.getState() == TableState.none || table.getState() == TableState.ready) {
            return;
        }
        QuartzFacade quartzFacade = (QuartzFacade) this.getFacade();
        ConfTimeoutInfoManager confTimeoutInfoManager = this.retrieveProxy(ConfTimeoutInfoManager.class);
        Player[] players = table.getPlayers();
        TableTokenService optTokenService = this.retrieveProxy(TableTokenService.class);
        if (table.getState() == TableState.jia_bei) {
            ConfTimeoutInfo jiaBeiTimeoutInfo = confTimeoutInfoManager.getJiaBeiInfo();
            List<byte[]> bytesList = new ArrayList<>(players.length);
            for (int i = 0; i < players.length; i++) {
                Player member = players[i];
                String token = optTokenService.createToken(table.getId(), table.getStep(), member.getSeatIndex());
                byte[] jiaBeiRespBytes = this.buildJiaBeiData(member, null, token, jiaBeiTimeoutInfo);
                bytesList.add(jiaBeiRespBytes);
            }
            TableDao deskdao = this.retrieveProxy(TableDao.class);
            deskdao.insertOrUpdateDesk(table);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("desk", table.getId());
            dataMap.put("param", bytesList);
            Date date = new Date(System.currentTimeMillis() + (jiaBeiTimeoutInfo.getMillsesc() + jiaBeiTimeoutInfo.getTimerOffset()));
            quartzFacade.sendMessage(new Message(OpCode.timer_auto_play, dataMap), "desk:" + table.getId(), date);
            return;
        }
        int step = table.getStep() + 1;
        table.setStep(step);
        Player nextPlayer;
        int nextOperateSeatIdx = table.getWaitOperateSeatIndex(); //下一个操作方
        if (table.getState() == TableState.qiang_dizhu) {
            while (table.isPassDiZhu(nextOperateSeatIdx)) {
                table.addOptRecord(new OptRecord(OptType.buqiangdizhu, nextOperateSeatIdx));
                nextOperateSeatIdx = Table.nextSeatIndex(nextOperateSeatIdx, table.getMaxPlayers());
            }
        }
        nextPlayer = table.getPlayerBySeatIndex(nextOperateSeatIdx);
        String token = optTokenService.createToken(table.getId(), table.getStep(), nextOperateSeatIdx);
        TableDao deskdao = this.retrieveProxy(TableDao.class);
        deskdao.insertOrUpdateDesk(table);

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("desk", table.getId());
        long timeoutMillsecs = 0;

        if (table.getState() == TableState.qiang_dizhu) {
            ConfTimeoutInfo diZhuTimeoutInfo = confTimeoutInfoManager.getDiZhuInfo();
            byte[] selfRespBytes = buildDizhuDataForSelf(table, nextPlayer, token, diZhuTimeoutInfo);
            byte[] otherRespBytes = buildDizhuDataForOther(nextPlayer, diZhuTimeoutInfo);
            for (Player member : players) {
                if (member.getSeatIndex() == nextOperateSeatIdx) {
                    this.sendData(member.getUid(), Crypto.isCrypto, Integer.parseInt(OpCode.cmd_show_opts), selfRespBytes);
                } else {
                    this.sendData(member.getUid(), Crypto.isCrypto, Integer.parseInt(OpCode.cmd_show_opts), otherRespBytes);
                }
            }
            dataMap.put("param", selfRespBytes);
            timeoutMillsecs = System.currentTimeMillis() + (millsecs(diZhuTimeoutInfo.getMillsesc(), nextPlayer, true) + diZhuTimeoutInfo.getTimerOffset());
        } else if (table.getState() == TableState.playing) {
            ConfTimeoutInfo chuOrPassTimeoutInfo = confTimeoutInfoManager.getChuOrPassInfo();
            byte[] selfRespBytes = buildPlayingDataForSelf(table, nextPlayer, token, chuOrPassTimeoutInfo);
            byte[] otherRespBytes = buildPlayingDataForOther(nextPlayer, chuOrPassTimeoutInfo);
            for (Player member : players) {
                if (member.getSeatIndex() == nextOperateSeatIdx) {
                    this.sendData(member.getUid(), Crypto.isCrypto, Integer.parseInt(OpCode.cmd_show_opts), selfRespBytes);
                } else {
                    this.sendData(member.getUid(), Crypto.isCrypto, Integer.parseInt(OpCode.cmd_show_opts), otherRespBytes);
                }
            }
            dataMap.put("param", selfRespBytes);
            timeoutMillsecs = System.currentTimeMillis() + (millsecs(chuOrPassTimeoutInfo.getMillsesc(), nextPlayer, true) + chuOrPassTimeoutInfo.getTimerOffset());
        }
        quartzFacade.sendMessage(new Message(OpCode.timer_auto_play, dataMap), "desk:" + table.getId(), new Date(timeoutMillsecs));
    }

    /**
     * 下一个操作方的数据，此数据仅发送给下一个操作方
     *
     * @param nextPlayer
     * @param token
     * @param diZhuTimeoutInfo
     * @return
     */
    public byte[] buildDizhuDataForSelf(Table table, Player nextPlayer, String token, ConfTimeoutInfo diZhuTimeoutInfo) {
        GameOpts.ScShowOpts.Builder builder = GameOpts.ScShowOpts.newBuilder();
        builder.setSeat(nextPlayer.getSeatIndex());
        builder.setToken(token);
        builder.setSecs((int) (millsecs(diZhuTimeoutInfo.getMillsesc(), nextPlayer) / 1000));
        builder.setWarnSecs((int) (warnMillsecs(diZhuTimeoutInfo.getWarnMillsec(), nextPlayer) / 1000));
        List<OptRecord> records = table.getRecords();
        boolean hasJiaoDizhu = false; //有人叫过地主就是抢地主了
        for (OptRecord or : records) {
            if (or.getOptType() == OptType.jiaodizhu) {
                hasJiaoDizhu = true;
                break;
            }
        }
        if (nextPlayer.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) {
            int jiabeiScore = AIBot.calcJiabeiScore(nextPlayer.getMycards());
            if (jiabeiScore < 3) {
                if (hasJiaoDizhu) {
                    builder.addOpt(OptType.buqiangdizhu.getId());
                } else {
                    builder.addOpt(OptType.bujiaodizhu.getId());
                }
            } else {
                if (hasJiaoDizhu) {
                    builder.addOpt(OptType.qiangdizhu.getId());
                    builder.addOpt(OptType.buqiangdizhu.getId());
                } else {
                    builder.addOpt(OptType.jiaodizhu.getId());
                    builder.addOpt(OptType.bujiaodizhu.getId());
                }
            }
        } else {
            if (hasJiaoDizhu) {
                builder.addOpt(OptType.qiangdizhu.getId());
                builder.addOpt(OptType.buqiangdizhu.getId());
            } else {
                builder.addOpt(OptType.jiaodizhu.getId());
                builder.addOpt(OptType.bujiaodizhu.getId());
            }
        }
        return builder.build().toByteArray();
    }

    /**
     * 下一个操作方的数据，此数据发送给除下一方玩家的其他两个玩家
     *
     * @param nextPlayer
     * @param diZhuTimeoutInfo
     * @return
     */
    public byte[] buildDizhuDataForOther(Player nextPlayer, ConfTimeoutInfo diZhuTimeoutInfo) {
        GameOpts.ScShowOpts.Builder builder = GameOpts.ScShowOpts.newBuilder();
        builder.setSeat(nextPlayer.getSeatIndex());
        builder.setSecs((int) (millsecs(diZhuTimeoutInfo.getMillsesc(), nextPlayer) / 1000));
        builder.setWarnSecs((int) (warnMillsecs(diZhuTimeoutInfo.getWarnMillsec(), nextPlayer) / 1000));
        return builder.build().toByteArray();
    }

    /**
     * 下一个操作方的数据，此数据仅发送给下一个操作方
     *
     * @param table
     * @param nextPlayer
     * @param token
     * @param chuOrPassTimeoutInfo
     * @return
     */
    public byte[] buildPlayingDataForSelf(Table table, Player nextPlayer, String token, ConfTimeoutInfo chuOrPassTimeoutInfo) {
        GameOpts.ScShowOpts.Builder builder = GameOpts.ScShowOpts.newBuilder();
        builder.setSeat(nextPlayer.getSeatIndex());
        builder.setToken(token);
        int nextRoundStartSeatIdx = table.getNextRoundStartSeatIndex(); //本轮出牌方
        List<int[]> cardsList = null;
        if (nextPlayer.getSeatIndex() == nextRoundStartSeatIdx) {
            //下一轮的开始
            cardsList = zhuDong(nextPlayer, table);
            builder.addOpt(OptType.chu.getId());
            builder.setIsRoundStart(1);
        } else {
            //压上家的牌
            ChuRecord chuRecord = table.getLastChuRecord();
            List<int[]> groups = beiDong(nextPlayer, chuRecord, table);
            if (groups == null || groups.isEmpty()) {
                builder.addOpt(OptType.pass.getId());
            } else {
                builder.addOpt(OptType.pass.getId());
                builder.addOpt(OptType.chu.getId());
                cardsList = groups;
            }
            builder.setIsRoundStart(0);
        }
        builder.setSecs((int) (millsecs(chuOrPassTimeoutInfo.getMillsesc(), nextPlayer) / 1000));
        builder.setWarnSecs((int) (warnMillsecs(chuOrPassTimeoutInfo.getWarnMillsec(), nextPlayer) / 1000));
        if (cardsList != null && !cardsList.isEmpty()) {
            for (int[] cards : cardsList) {
                Game.Cards.Builder cardsBuilder = Game.Cards.newBuilder();
                for (int card : cards) {
                    cardsBuilder.addCard(card);
                }
                builder.addGroup(cardsBuilder.build());
            }
        }
        return builder.build().toByteArray();
    }

    /**
     * 下一个操作方的数据，此数据发送给除下一方玩家的其他两个玩家
     *
     * @param nextPlayer
     * @param chuOrPassTimeoutInfo
     * @return
     */
    public byte[] buildPlayingDataForOther(Player nextPlayer, ConfTimeoutInfo chuOrPassTimeoutInfo) {
        GameOpts.ScShowOpts.Builder builder = GameOpts.ScShowOpts.newBuilder();
        builder.setSeat(nextPlayer.getSeatIndex());
        builder.setSecs((int) (millsecs(chuOrPassTimeoutInfo.getMillsesc(), nextPlayer) / 1000));
        builder.setWarnSecs((int) (warnMillsecs(chuOrPassTimeoutInfo.getWarnMillsec(), nextPlayer) / 1000));
        return builder.build().toByteArray();
    }

    /**
     * 指示玩家加倍操作
     *
     * @param player
     * @param token
     * @param jiaBeiTimeoutInfo
     * @return
     */
    public byte[] buildJiaBeiData(Player player, UserInfo userInfo, String token, ConfTimeoutInfo jiaBeiTimeoutInfo) {
        GameOpts.ScShowOpts.Builder builder = GameOpts.ScShowOpts.newBuilder();
        if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) {
            int jiabeiScore = AIBot.calcJiabeiScore(player.getMycards());
            if (jiabeiScore < 3) {
                builder.addOpt(OptType.buJiabei.getId());
            } else {
                if (jiabeiScore >= 5) {
                    builder.addOpt(OptType.jiabei1.getId());
                    if (jiabeiScore >= 7) {
                        builder.addOpt(OptType.jiabei2.getId());
                    }
                }
            }
        } else {
            if (userInfo == null) {
                UserDao userDao = this.retrieveProxy(UserDao.class);
                if (userDao == null) {
                    userDao = FacadeFactory.getFacade().retrieveProxy(UserDao.class);
                }
                userInfo = userDao.getUserInfo(player.getUid());
            }
            builder.addOpt(OptType.jiabei1.getId());
            List<ItemInfo> itemList = ItemUtils.parseItemList(userInfo.getItems());
            ItemUtils.filterItems(itemList);
            for (ItemInfo itemInfo : itemList) {
                if (itemInfo.getCid() == 8 && itemInfo.getNum() > 0) {
                    builder.addOpt(OptType.jiabei2.getId());
                    builder.setJiaBeiNum(itemInfo.getNum());
                    break;
                }
            }
        }
        builder.setSecs((int) (millsecs(jiaBeiTimeoutInfo.getMillsesc(), player) / 1000));
        builder.setWarnSecs((int) (warnMillsecs(jiaBeiTimeoutInfo.getWarnMillsec(), player) / 1000));
        builder.setSeat(player.getSeatIndex());
        builder.setToken(token);
        byte[] respBytes = builder.build().toByteArray();
        this.sendData(player.getUid(), Crypto.isCrypto, Integer.parseInt(OpCode.cmd_show_opts), respBytes);
        return respBytes;
    }

    /**
     * @param millsecs
     * @param player
     * @param forTimer 是否用于定时调度，为true表示定时任务实际超时执行的时间，否则为展示给用户看的倒计时时间
     * @return
     */
    private long millsecs(long millsecs, Player player, boolean forTimer) {
        if (player == null) return millsecs;
        if (forTimer) { //机器人
            if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) {
                return MathUtils.random(1000, 2000);
            }
        }
        if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) {
            return millsecs;
        }
        if (player.getTimeoutCount() == TrusteeType.system_auto.getTimeoutCount()) return 0;
        if (player.getTimeoutCount() == TrusteeType.custom.getTimeoutCount()) return 0;
        if (player.getTimeoutCount() == TrusteeType.exit.getTimeoutCount()) return 0;
        return millsecs;
    }

    /**
     * 展示给用户看到的倒计时时间
     *
     * @param millsecs
     * @param player
     * @return
     */
    private long millsecs(long millsecs, Player player) {
        return millsecs(millsecs, player, false);
    }

    private long warnMillsecs(long warnMillsecs, Player player) {
        if (player == null) return warnMillsecs;
        if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) return warnMillsecs;
        if (player.getTimeoutCount() > TrusteeType.system_auto.getTimeoutCount()) return 0;
        return warnMillsecs;
    }

    private List<int[]> zhuDong(Player player, Table table) {
        Cards mycards = player.getMycards();
        GameMode mode = GameMode.getModeById(table.getMode());
        if (mode == GameMode.LAIZI || (mode == GameMode.LIAN_WIN && table.getPlace() == GameMode.LAIZI.getId())) {
            return AILaizi.zhuDong(mycards, table.getLaiziPoint());
        }
        if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()) {
            int nextSeatIdx = Table.nextSeatIndex(player.getSeatIndex(), table.getMaxPlayers());
            Player nextPlayer = table.getPlayerBySeatIndex(nextSeatIdx);
            Cards nextPlayerCards = nextPlayer.getMycards();
            if (player.getSeatIndex() == table.getDizhuSeatIndex()) {
                //我是地主
                if (nextPlayerCards.getCardCount() == 1) { //下家仅剩一张牌，尽可能不出单牌，必须出单牌时往最大的出
                    return AIBot.zhuDong(mycards, true);
                }
            } else {
                //我不是地主
                if (nextSeatIdx == table.getDizhuSeatIndex()) {
                    //下一方是地主，上一方则为队友
                    if (nextPlayerCards.getCardCount() == 1) { //下家仅剩一张牌，尽可能不出单牌，必须出单牌时往最大的出
                        return AIBot.zhuDong(mycards, true);
                    } else {
                        int prevSeatIdx = Table.prevSeatIndex(player.getSeatIndex(), table.getMaxPlayers());
                        Player prevPlayer = table.getPlayerBySeatIndex(prevSeatIdx);
                        Cards prevHandCards = prevPlayer.getMycards();
                        if (prevHandCards.getCardCount() == 1) { //上家队友仅剩一张牌时，出最小的单牌，仅限单牌，无单牌则正常出牌
                            return AIBot.zhuDong(mycards, false);
                        }
                    }
                } else {
                    //下一方为队友
                    if (nextPlayerCards.getCardCount() == 1) { //队友下家仅剩一张牌，则出自己最小的一张
                        int[] cards = nextPlayerCards.getCards();
                        if (cards != null && cards.length > 0) {
                            int card = cards[0];
                            Pile selfMinPile = mycards.getPileByIndex(mycards.getMinValidIndex());
                            if (selfMinPile.compare(card) < 0) {
                                cards = selfMinPile.getCards();
                                List<int[]> result = new ArrayList<>(1);
                                result.add(new int[]{cards[0]});
                                return result;
                            }
                            //自己最小的都比队友的最后一张要大，没办法，继续走起
                        }
                    }
                }
            }
            return AIBot.zhuDong(mycards, false);
        }
        return AI.zhuDong(mycards);
    }

    /**
     * 压上家的牌
     *
     * @param player     等待操作的玩家
     * @param lastRecord 最后一方出的牌
     * @return 可出的牌型集合
     */
    private List<int[]> beiDong(Player player, ChuRecord lastRecord, Table table) {
        Cards mycards = player.getMycards();
        Cards prevChuCards = new Cards();
        int[] _prevChuCards = lastRecord.getDiscardCards();
        for (int card : _prevChuCards) {
            card = Cards.spliteForeCard(card);
            prevChuCards.addCard(card);
        }
        int prevChuSeat = lastRecord.getSeatIndex();
        CardType prevChuType = lastRecord.getType();
        boolean lianZhaMode = false;
        if (table.getMode() == GameMode.LIAN_ZHA.getId()
                || (table.getMode() == GameMode.LIAN_WIN.getId() && table.getPlace() == GameMode.LIAN_ZHA.getId())) {
            lianZhaMode = true;
        }
        GameMode mode = GameMode.getModeById(table.getMode());
        if (player.getTimeoutCount() != TrusteeType.robot.getTimeoutCount()) {
            if (mode == GameMode.LAIZI || (mode == GameMode.LIAN_WIN && table.getPlace() == GameMode.LAIZI.getId())) {
                return AILaizi.beiDong(mycards, prevChuCards, prevChuType, table.getLaiziPoint());
            }
            return AI.beiDong(mycards, prevChuCards, prevChuType, lianZhaMode);
        }
        //以下处理机器人相关逻辑
        boolean isOver = AI.isOver(mycards, prevChuCards, prevChuType, lianZhaMode);
        if (isOver) {
            //只剩一手牌，且比上家出的牌大，直接出
            List<int[]> list = new ArrayList<>(1);
            list.add(mycards.getCards());
            return list;
        }
        int prevSeatIdx = Table.prevSeatIndex(player.getSeatIndex(), table.getMaxPlayers());
        int nextSeatIdx = Table.nextSeatIndex(player.getSeatIndex(), table.getMaxPlayers());
        Player nextPlayer = table.getPlayerBySeatIndex(nextSeatIdx);
        Cards nextPlayerCards = nextPlayer.getMycards();
        if (player.getSeatIndex() == table.getDizhuSeatIndex()) {
            //我是地主
            if (prevChuType == CardType.dan && nextPlayerCards.getCardCount() == 1) { //下家仅剩一张牌，最大的单牌出
                return AIBot.beiDong(mycards, prevChuCards, prevChuType, true, lianZhaMode);
            }
        } else {
            //我不是地主
            if (nextSeatIdx == table.getDizhuSeatIndex()) {
                //下一方是地主，上一方则为队友
                if (prevChuSeat == prevSeatIdx) { //上次出牌方为队友
                    if (prevChuType != CardType.dan && prevChuType != CardType.dui) {
                        //上家打出的牌是除了单牌及对子以外的牌型，直接PASS
                        return null;
                    }
                    if (prevChuType == CardType.dan) { //上家打出的单牌
                        if (nextPlayerCards.getCardCount() == 1) { //地主下家仅剩一张牌, 往大的单牌出
                            return AIBot.beiDong(mycards, prevChuCards, prevChuType, true, lianZhaMode);
                        } else {
                            //能跟上家的单牌则跟，但要以2来跟牌时，直接pass
                            List<int[]> result = AIBot.beiDong(mycards, prevChuCards, prevChuType, false, lianZhaMode);
                            if (result != null && !result.isEmpty()) {
                                int[] cards = result.get(0);
                                int card = cards[0];
                                int point = Cards.getPoint(card);
                                if (point == 2 || point == 0 || cards.length == 4) {
                                    if (mycards.getCardCount() == cards.length) {
                                        return result;
                                    }
                                    return null;
                                }
                            }
                            return result;
                        }
                    }
                    //能跟上家的对牌则跟，但要以AA来跟牌时，直接pass
                    List<int[]> result = AIBot.beiDong(mycards, prevChuCards, prevChuType, false, lianZhaMode);
                    if (result != null && !result.isEmpty()) {
                        int[] cards = result.get(0);
                        int card = cards[0];
                        int point = Cards.getPoint(card);
                        if (point == 14 || point == 2 || point == 0 || cards.length == 4) {
                            if (mycards.getCardCount() == cards.length) {
                                return result;
                            }
                            return null;
                        }
                    }
                    return result;
                } else {
                    //上次出牌方为地主，正常流程出牌
                }
            } else {
                //上一方则为地主，下一方为队友
                if (prevChuSeat == nextSeatIdx) { //出牌方为队友
                    if (prevChuType != CardType.dan && prevChuType != CardType.dui) {
                        //上家打出的牌是除了单牌及对子以外的牌型，直接PASS
                        return null;
                    }
                    if (prevChuType == CardType.dan) { //上家打出的单牌
                        //能跟上家的单牌则跟，但要以2来跟牌时，直接pass
                        List<int[]> result = AIBot.beiDong(mycards, prevChuCards, prevChuType, false, lianZhaMode);
                        if (result != null && !result.isEmpty()) {
                            int[] cards = result.get(0);
                            int card = cards[0];
                            int point = Cards.getPoint(card);
                            if (point == 2 || point == 0 || cards.length == 4) {
                                if (mycards.getCardCount() == cards.length) {
                                    return result;
                                }
                                return null;
                            }
                        }
                        return result;
                    }
                    //能跟上家的对牌则跟，但要以AA来跟牌时，直接pass
                    List<int[]> result = AIBot.beiDong(mycards, prevChuCards, prevChuType, false, lianZhaMode);
                    if (result != null && !result.isEmpty()) {
                        int[] cards = result.get(0);
                        int card = cards[0];
                        int point = Cards.getPoint(card);
                        if (point == 14 || point == 2 || point == 0 || cards.length == 4) {
                            if (mycards.getCardCount() == cards.length) {
                                return result;
                            }
                            return null;
                        }
                    }
                    return result;
                } else {
                    //出牌方为地主，按普通跟牌规则跟牌
                }
                //直接走下面，此处代码不用写
            }
        }
        return AIBot.beiDong(mycards, prevChuCards, prevChuType, false, lianZhaMode);
    }

}
